/**
 * moduł z różnego rodzaju funkcjami pomocniczymi
 * @module Tools
 */
App.Tools = angular.module('Tools', []).
/**
 * klasa zapewniająca dostęp do potrzebnych funkcji
 * @class ToolsService
 * @extends angular.service
 */
service('Tools.Service', [
			'Tools.Copier', 
			'Tools.isArray', 
			'Tools.map', 
			'Tools.inject', 
			'Tools.lpad',
function (Copier, isArray, map, inject, lpad) {
	/**
	 * kopiuje konkretne właściwości z jednego obiektu do drugiego
	 * @param  {Object} src  obiekt źródłowy
	 * @param  {Object} dest obiekt docelowy
	 * @param  {Array|String} [mode] tryb kopiowania, dostępne są:
	 * - _all_ - kopiowane są wszystkie właściwości dostępne przez pętlę `for .. in`
	 * - tablica z nazwami atrybutów do skopiowania
	 * - niepodanie argumentu, wtedy ustawiana jest opcja _all_
	 * @method copy
	 * @public
	 */
	this.copy = function (src, dest, mode) {
		var copier = new Copier();
		if(mode === undefined) {
			mode = 'all';
		}
		copier.setSource(src);
		copier.setDestination(dest);
		copier.setProperties(mode);
		copier.copy();
	};
	/**
	 * w celu dokumentacji patrz: `Tools.isArray.main`
	 * jest to ta sama funkcja
	 * @method isArray
	 * @public
	 */
	this.isArray = function () {
		return isArray;
	};
	/**
	 * w celu dokumentacji patrz: `Tools.map.main`
	 * @method map
	 * @public
	 */
	this.map = map;
	/**
	 * dokumentacja jest w `Tools.inject.main`
	 * @method inject
	 * @public
	 */
	this.inject = inject;
	/**
	 * dokumentacja jest w `Tools.lpad.main`
	 * @method lpad
	 * @public
	 */
	this.lpad = lpad;
}]).
/**
 * fabryka udostępniająca klasę `Copier`
 *
 * właśnie z tej klasy korzysta `ToolsService.copy`
 * @class Tools.Copier
 * @extends angular.factory
 */
factory('Tools.Copier', ['Tools.isArray', function (isArray) {
	/**
	 * klasa kopiująca właściwości z obiektu do obiektu
	 * @class Copier
	 */
	function Copier () {
		/**
		 * uchwyt do `this` na potrzeby zagnieżdżeń zakresów
		 * @type {Copier}
		 * @property that
		 * @private
		 */
		var that = this,
		/**
		 * właściwości do skopiowania
		 * @type {mixed}
		 * @property properties
		 * @private
		 */
			properties = false,
		/**
		 * obiekt źródłowy	
		 * @type {Object}
		 * @property source
		 * @private
		 */
			source = false,
		/**
		 * obiekt docelowy
		 * @type {Object}
		 * @property destination
		 * @private
		 */
			destination = false,
		/**
		 * obiekt przechowujący referencje do funkcji kopiujących w określonych trybach
		 * @type {Object}
		 * @property propertiesTypes
		 * @private
		 */
			propertiesTypes = {
				array: copyArray,
				string: copySingle,
				all: copyAll
			};
		/**
		 * ustawia właściwości do skopiowania na podane
		 * @param  {Array|String} [props] tryb kopiowania, dostępne są:
		 * - _all_ - kopiowane są wszystkie właściwości dostępne przez pętlę `for .. in`
		 * - tablica z nazwami atrybutów do skopiowania
		 * - niepodanie argumentu, wtedy ustawiana jest opcja _all_
		 * @method setProperties
		 * @public
		 */
		function setProperties (props) {
			properties = props;
		}
		/**
		 * ustawia obiekt źródłowy na podany
		 * @param {Object} src
		 * @method setSource
		 * @public
		 */
		function setSource (src) {
			source = src;
		}
		/**
		 * ustawia obiekt docelowy na podany
		 * @param {Object} dest
		 * @method setDestination
		 * @public
		 */
		function setDestination (dest) {
			destination = dest;
		}
		/**
		 * główna funkcja uruchamiająca kopiowanie
		 * @method copy
		 * @public
		 */
		function copy () {
			var copyingFunc = propertiesTypes[checkPropertiesType()];
			return copyingFunc(properties);
		}
		/**
		 * sprawdza sposób, w jaki zostały zadane właściwości do skopiowania
		 * @return {String} nazwa sposobu
		 * @method checkPropertiesType
		 * @private
		 */
		function checkPropertiesType () {
			if(isArray(properties)) {
				return 'array';
			} else if (typeof properties === 'string') {
				if (properties === 'all') {
					return 'all';
				} else {
					return 'string';
				}
			}
		}
		/**
		 * kopiuje, gdy właściwości zadane są wprost w tablicy
		 * @param  {Array} arr
		 * @method copyAray
		 * @private
		 */
		function copyArray (arr) {
			var i,l;
			for(i=0, l=arr.length; i<l; ++i) {
				copySingle(arr[i]);
			}
		}
		/**
		 * kopiuje pojedynczą właściwość o podanej nazwie
		 * @param  {String} name 
		 * @method copySingle
		 * @private
		 */
		function copySingle (name) {
			destination[name] = source[name];
		}
		/**
		 * kopiuje wszystkie właściwości dostępne przez `for .. in`
		 * @method copyAll
		 * @private
		 */
		function copyAll (props) {
			for(prop in source) {
				copySingle(prop);
			}
		}
		/**
		 * konstruktor
		 * @constructor
		 * @method construct
		 * @public
		 */
		(function construct () {
			that.setProperties = setProperties;
			that.setSource = setSource;
			that.setDestination = setDestination;
			that.copy = copy;
		})();
	}
	return Copier;
}]).
/**
 * fabryka udostępniająca funkcję `isArray`
 * @class Tools.isArray
 * @extends angular.factory
 */
factory('Tools.isArray', function () {
	/**
	 * funkcja sprawdza, czy obiekt jest tablicą
	 * @param  {Object}  obj obiekt do sprawdzenia
	 * @return {Boolean} 
	 * @method main
	 * @public
	 */
	function isArray (obj) {
		return (Object.prototype.toString.call(obj) === '[object Array]');
	};
	return isArray;
}).
/**
 * fabryka udostępniająca funkcję `map`
 * @class Tools.map
 * @extends angular.factory
 */
factory('Tools.map', function () {
	/**
	 * funkcja aplikująca funkcję do każdego elementu tablicy
	 * @param  {Array} arr  tablica źródłowa
	 * @param  {Function} func funkcja, musi przyjmować jeden parametr będący elementem tablicy
	 * @return {Array}      nowa tablica
	 * @method main
	 * @public
	 */
	return function map (arr, func) {
		var i, l, newArr = [];
		for(i=0, l=arr.length; i<l; ++i) {
			newArr[i] = func(arr[i], i);
		}
		return newArr;
	}
}).
/**
 * fabryka udostępniająca funkcję `inject`
 * @class Tools.inject
 * @extends angular.factory
 */
factory('Tools.inject', ['$injector', function ($injector) {
	/**
	 * funkcja umożliwia wstrzyknięcie wybranego elementu angulara w trakcie wykonywania skryptu
	 * umożliwia pozbycie się pętli wśród zależności definiowanych jawnie
	 * @param  {String} name nazwa elementu
	 * @return {angular} element pod daną nazwą
	 * @method main
	 * @public
	 */
	function inject (name) {
		return $injector.invoke([name, function (n) {
			return n;
		}])
	}
	return inject;
}]).
/**
 * fabryka udostępniająca funkcję `lpad`
 * @class Tools.lpad
 * @extends angular.factory
 */
factory('Tools.lpad', [function () {
	/**
	 * metoda dodaje z lewej strony napisu wypełnienie do określonej długości dodając kolejne znaki `paddingStr`
	 * @param  {String} str        napis do wypełnienia
	 * @param  {String} paddingStr napis wypełniający
	 * @param  {Number} length     oczekiwana długość napisu
	 * @return {String}
	 * @method main
	 * @public
	 */
	function lpad (str, paddingStr, length) {
		var actualLength = (str+'').length,
			difference = length,
			i, l,
			pad = '';
		difference -= actualLength;
		for(i=0, l=difference; i<l; ++i) {
			pad += paddingStr[i%paddingStr.length];
		}
		return pad+str;
	}
	return lpad;
}])